Typical HAL FMAC use cases for migration

This section provides information about the typical HAL FMAC use cases for migration.

  • On the FMAC peripheral, both input and output operations can be configured using a variety of modes, including polling, interrupt, DMA, or handled by external peripheral. These modes can be independently selected for input and output, allowing for flexible combinations between FIFO input writing and FIFO output reading (as shown in the table of typical combinations).

  • In HAL1, these modes were configured using the InputAccess and OutputAccess fields within the FMAC_FilterConfigTypeDef structure.

  • In HAL2, input data management is performed through the following APIs:

    • HAL_FMAC_WriteX1()

    • HAL_FMAC_WriteX1_IT()

    • HAL_FMAC_WriteX1_DMA()

    • HAL_FMAC_WriteX1_DMA_Opt()

When delegating input data handling to another peripheral, these WriteX1 APIs are not called directly.

  • Similarly, output data management uses the APIs:

    • HAL_FMAC_ReadY()

    • HAL_FMAC_ReadY_IT()

    • HAL_FMAC_ReadY_DMA()

    • HAL_FMAC_ReadY_DMA_Opt()

If output data handling is assigned to another peripheral, the ReadY APIs are not called directly either.

  • Typical combinations:

Input Mode (HAL1)

Output Mode (HAL1)

HAL1 Configuration ( InputAccess , OutputAccess )

HAL2 Handling (Input / Output APIs)

Polling

Polling

InputAccess = FMAC_BUFFER_ACCESS_POLLING OutputAccess = FMAC_BUFFER_ACCESS_POLLING

HAL_FMAC_WriteX1() / HAL_FMAC_ReadY()

Polling

Interrupt

InputAccess = FMAC_BUFFER_ACCESS_POLLING OutputAccess = FMAC_BUFFER_ACCESS_IT

HAL_FMAC_WriteX1() / HAL_FMAC_ReadY_IT()

Polling

DMA

InputAccess = FMAC_BUFFER_ACCESS_POLLING OutputAccess = FMAC_BUFFER_ACCESS_DMA

HAL_FMAC_WriteX1() / HAL_FMAC_ReadY_DMA() or HAL_FMAC_ReadY_DMA_Opt()

Interrupt

Polling

InputAccess = FMAC_BUFFER_ACCESS_IT OutputAccess = FMAC_BUFFER_ACCESS_POLLING

HAL_FMAC_WriteX1_IT() / HAL_FMAC_ReadY()

Interrupt

Interrupt

InputAccess = FMAC_BUFFER_ACCESS_IT OutputAccess = FMAC_BUFFER_ACCESS_IT

HAL_FMAC_WriteX1_IT() / HAL_FMAC_ReadY_IT()

Interrupt

DMA

InputAccess = FMAC_BUFFER_ACCESS_IT OutputAccess = FMAC_BUFFER_ACCESS_DMA

HAL_FMAC_WriteX1_IT() / HAL_FMAC_ReadY_DMA() or HAL_FMAC_ReadY_DMA_Opt()

DMA

Polling

InputAccess = FMAC_BUFFER_ACCESS_DMA OutputAccess = FMAC_BUFFER_ACCESS_POLLING

HAL_FMAC_WriteX1_DMA() or HAL_FMAC_WriteX1_DMA_Opt() / HAL_FMAC_ReadY()

DMA

Interrupt

InputAccess = FMAC_BUFFER_ACCESS_DMA OutputAccess = FMAC_BUFFER_ACCESS_IT

HAL_FMAC_WriteX1_DMA() or HAL_FMAC_WriteX1_DMA_Opt() / HAL_FMAC_ReadY_IT()

DMA

DMA

InputAccess = FMAC_BUFFER_ACCESS_DMA OutputAccess = FMAC_BUFFER_ACCESS_DMA

HAL_FMAC_WriteX1_DMA() or HAL_FMAC_WriteX1_DMA_Opt() / HAL_FMAC_ReadY_DMA() or HAL_FMAC_ReadY_DMA_Opt()

External Device (e.g., ADC)

Any

InputAccess = FMAC_BUFFER_ACCESS_NONE OutputAccess = [any valid mode]

Do not call Write APIs / Use appropriate Read API or external handling

Any

External Device

InputAccess = [any valid mode] OutputAccess = FMAC_BUFFER_ACCESS_NONE

Use appropriate Write API or external handling / Do not call Read APIs

Use case: FIR or IIR (interrupt input X1, DMA output Y)

This use case shows an FMAC FIR/IIR filter where the X1 input FIFO is filled in interrupt mode and the Y output FIFO is read in DMA mode.

Sequence diagram

     @startuml
     title: FIR filter with :\n  - input/X1 managed with interrupt process \n  - output/Y managed with DMA process

     participant App as "User application"
     participant App
     participant "<font color=Black><b>HAL FMAC</b></font>" as FMAC
     participant "<font color=green><b>HAL DMA</b></font>" as DMA
     participant "FMACx_IRQHandler"
     participant "DMAx_IRQHandler"

     App -> FMAC : HAL_FMAC_Init()
     App <-- FMAC : HAL_OK

     group FMAC System initialization: can be called before FMAC HAL initialization
     App -[hidden]>FMAC
     end

     App -> FMAC : HAL_FMAC_FilterConfig()
     note right : The interrupt mode configuration for input X1 is carried out in this function by setting FMAC_BUFFER_ACCESS_IT\nin the InputAccess parameter within FMAC_FilterConfigTypeDef structure.\nThe DMA mode configuration for output Y is carried out in this function by setting FMAC_BUFFER_ACCESS_DMA\nin the OutputAccess parameter within FMAC_FilterConfigTypeDef structure.
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_AppendFilterData()
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_FilterStart()
     App <-- FMAC :HAL_OK

     FMAC <- "FMACx_IRQHandler" : HAL_FMAC_IRQHandler()
     FMAC --> "FMACx_IRQHandler"

     FMAC-> App : HAL_FMAC_GetDataCallback()
     FMAC<-- App

     "DMAx_IRQHandler" <- : DMAx half complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_HalfOutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_OutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     App -> FMAC : HAL_FMAC_FilterStop()
     App <-- FMAC :HAL_OK

     App->FMAC : HAL_FMAC_DeInit()
     App<--FMAC

     @enduml

Code snippet

HAL1

HAL2

HAL_FMAC_Init(&hfmac)

sFmacConfig.InputBaseAddress  = INPUT_BUFFER_BASE;
sFmacConfig.InputBufferSize   = INPUT_BUFFER_SIZE;
sFmacConfig.InputThreshold    = INPUT_THRESHOLD;
sFmacConfig.CoeffBaseAddress  = COEFFICIENT_BUFFER_BASE;
sFmacConfig.CoeffBufferSize   = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.OutputBaseAddress = OUTPUT_BUFFER_BASE;
sFmacConfig.OutputBufferSize  = OUTPUT_BUFFER_SIZE;
sFmacConfig.OutputThreshold   = OUTPUT_THRESHOLD;
sFmacConfig.pCoeffA           = aFilterCoeffA;
sFmacConfig.CoeffASize        = COEFF_VECTOR_A_SIZE;
sFmacConfig.pCoeffB           = aFilterCoeffB;
sFmacConfig.CoeffBSize        = COEFF_VECTOR_B_SIZE;
sFmacConfig.Filter            = FMAC_FUNC_IIR_DIRECT_FORM_1;
sFmacConfig.InputAccess       = FMAC_BUFFER_ACCESS_IT;
sFmacConfig.OutputAccess      = FMAC_BUFFER_ACCESS_DMA;
sFmacConfig.Clip              = FMAC_CLIP_DISABLED;
sFmacConfig.P                 = COEFF_VECTOR_B_SIZE;
sFmacConfig.Q                 = COEFF_VECTOR_A_SIZE;
sFmacConfig.R                 = GAIN;

HAL_FMAC_FilterConfig(&hfmac, &sFmacConfig);

HAL_FMAC_AppendFilterData(&hfmac, Input);
HAL_FMAC_FilterStart(&hfmac, Output, OutputDataSize);

HAL_FMAC_FilterStop(&hfmac);

HAL_FMAC_DeInit(&gh_fmac);
HAL_FMAC_Init(&hfmac, (hal_fmac_t)FMAC);

sFmacConfig.x1_buf.base_half_word = INPUT_BUFFER_BASE;
sFmacConfig.x1_buf.size_half_word = INPUT_BUFFER_SIZE;
sFmacConfig.x2_buf.base_half_word = COEFFICIENT_BUFFER_BASE;
sFmacConfig.x2_buf.size_half_word = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.base_half_word  = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.size_half_word  = OUTPUT_BUFFER_BASE;

HAL_FMAC_SetConfig(&hfmac, &sFmacConfig);

data.coeff_a_size_half_word = COEFF_VECTOR_A_SIZE;
data.p_coeff_a              = aFilterCoeffA;
data.coeff_b_size_half_word = COEFF_VECTOR_B_SIZE;
data.p_coeff_b              = aFilterCoeffB;

HAL_FMAC_PreloadX2(&hfmac, &data);

HAL_FMAC_WriteX1_IT(&hfmac, Input , size_half_word);
HAL_FMAC_ReadY_DMA(&hfmac, Output, OutputDataSize);

HAL_FMAC_StartFilterIIR(&hfmac, COEFF_VECTOR_B_SIZE, COEFF_VECTOR_A_SIZE, GAIN, HAL_FMAC_OPT_IT_FILTER_SATURATION_ERROR);

HAL_FMAC_StopFilter(&hfmac);

HAL_FMAC_DeInit(&gh_fmac);

Key changes

  1. HAL2 replaces the HAL1 FMAC_FilterConfigTypeDef (InputAccess/OutputAccess) mode selection with explicit IO APIs (HAL_FMAC_WriteX1_* and HAL_FMAC_ReadY_*).

  2. HAL2 uses a clearer split between configuration (HAL_FMAC_SetConfig()) and runtime processes (Write X1 / Read Y / Start filter).


Use case: FIR or IIR (ADC input X1, DMA output Y)

This use case shows an FMAC FIR/IIR filter where the X1 input FIFO is fed by the ADC (external peripheral) and the Y output FIFO is read in DMA mode.

Sequence diagram

     @startuml
     title: FIR filter with :\n  - input/X1 managed by the ADC driver \n  - output/Y managed with DMA  process

     participant App as "User application"
     participant App
     participant "<font color=Black><b>HAL FMAC</b></font>" as FMAC
     participant "<font color=green><b>HAL DMA</b></font>" as DMA
     participant "<font color=red><b>HAL ADC</b></font>" as ADC
     participant "FMACx_IRQHandler"
     participant "DMAx_IRQHandler"

     App -> ADC : HAL_ADC_Init
     App <-- ADC : HAL_OK

     App -> FMAC : HAL_FMAC_Init
     App <-- FMAC : HAL_OK

     group ADC System initialization: can be called before ADC HAL initialization
     App -[hidden]>ADC
     end

     group FMAC System initialization: can be called before FMAC HAL initialization
     App -[hidden]>FMAC
     end

     App -> FMAC : HAL_FMAC_FilterConfig()
     note right : The ADC management configuration for input X1 is carried out in this function by setting FMAC_BUFFER_ACCESS_NONE\nin the InputAccess parameter within FMAC_FilterConfigTypeDef structure.\nThe DMA mode configuration for output Y is carried out in this function by setting FMAC_BUFFER_ACCESS_DMA\nin the OutputAccess parameter within FMAC_FilterConfigTypeDef structure.
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_AppendFilterData()
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_FilterStart()
     App <-- FMAC :HAL_OK

     App -> ADC : HAL_ADC_Start_DMA
     note right : Start ADC conversion and store the result in FMAC Write Data register address (@WDATA)
     App <-- ADC : HAL_OK

     "DMAx_IRQHandler" <- : DMAx half complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> ADC : XferCpltCallback
     ADC-> App : HAL_ADC_ConvHalfCpltCallback()
     "DMA" <-- ADC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> ADC : XferCpltCallback
     ADC-> App : HAL_ADC_ConvCpltCallback()
     "DMA" <-- ADC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx half complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_HalfOutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_OutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     App -> FMAC : HAL_FMAC_FilterStop()
     App <-- FMAC :HAL_OK

     App->FMAC : HAL_FMAC_DeInit()
     App<--FMAC

     @enduml

Code snippet

HAL1

HAL2

ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};

/* FMAC WDATA register address (for DMA transfer) */
#define FMAC_WDATA (0x40021418)

hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_HRTIM_TRG1;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;

if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
  Error_Handler();
}

multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
  Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_1;
sConfig.Offset = REF;
sConfig.OffsetSign = ADC_OFFSET_SIGN_NEGATIVE;
sConfig.OffsetSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
  Error_Handler();
}

HAL_FMAC_Init(&hfmac);

sFmacConfig.InputBaseAddress  = INPUT_BUFFER_BASE;
sFmacConfig.InputBufferSize   = INPUT_BUFFER_SIZE;
sFmacConfig.InputThreshold    = INPUT_THRESHOLD;
sFmacConfig.CoeffBaseAddress  = COEFFICIENT_BUFFER_BASE;
sFmacConfig.CoeffBufferSize   = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.OutputBaseAddress = OUTPUT_BUFFER_BASE;
sFmacConfig.OutputBufferSize  = OUTPUT_BUFFER_SIZE;
sFmacConfig.OutputThreshold   = OUTPUT_THRESHOLD;
sFmacConfig.pCoeffA           = aFilterCoeffA;
sFmacConfig.CoeffASize        = COEFF_VECTOR_A_SIZE;
sFmacConfig.pCoeffB           = aFilterCoeffB;
sFmacConfig.CoeffBSize        = COEFF_VECTOR_B_SIZE;
sFmacConfig.Filter            = FMAC_FUNC_IIR_DIRECT_FORM_1;
sFmacConfig.InputAccess       = FMAC_BUFFER_ACCESS_NONE;
sFmacConfig.OutputAccess      = FMAC_BUFFER_ACCESS_DMA;
sFmacConfig.Clip              = FMAC_CLIP_DISABLED;
sFmacConfig.P                 = COEFF_VECTOR_B_SIZE;
sFmacConfig.Q                 = COEFF_VECTOR_A_SIZE;
sFmacConfig.R                 = GAIN;

HAL_FMAC_FilterConfig(&hfmac, &sFmacConfig);

HAL_FMAC_FilterStart(&hfmac, Output, OutputDataSize);

/* Assign FMAC Write Data register (@WDATA) to Fmac_Wdata */
Fmac_Wdata = (uint32_t *) FMAC_WDATA;

/* Start ADC and DMA */
HAL_ADC_Start_DMA(&hadc1, Fmac_Wdata, OUTPUT_BUFFER_SIZE*4);

HAL_FMAC_FilterStop(&hfmac);

HAL_FMAC_DeInit(&gh_fmac);
hal_adc_config_t adc_config;

HAL_ADC_Init(&hADC1, HAL_ADC1);

adc_config.resolution          = HAL_ADC_RESOLUTION_12_BIT;
adc_config.sampling_mode       = HAL_ADC_SAMPLING_MODE_NORMAL;
HAL_ADC_SetConfig(&hADC1, &adc_config);

hal_adc_reg_config_t reg_config;
reg_config.trigger_src        = HAL_ADC_REG_TRIG_SOFTWARE_START;
reg_config.trigger_edge       = HAL_ADC_REG_TRIG_EDGE_NONE;
reg_config.sequencer_length   = 1;
reg_config.sequencer_discont  = HAL_ADC_REG_SEQ_DISCONT_DISABLE;
reg_config.continuous         = HAL_ADC_REG_CONV_SINGLE;
reg_config.overrun            = HAL_ADC_REG_OVR_DATA_OVERWRITTEN;
reg_config.data_transfer      = HAL_ADC_REG_DATA_TRANSFER_NONE;
HAL_ADC_REG_SetConfig(&hADC1, &reg_config);

hal_adc_channel_config_t adc_channel_config;
adc_channel_config.group           = HAL_ADC_GROUP_REGULAR;
adc_channel_config.sequencer_rank  = 1;
adc_channel_config.input_mode      = HAL_ADC_IN_SINGLE_ENDED;
adc_channel_config.sampling_time   = HAL_ADC_SAMPLING_TIME_2CYCLES_5;
HAL_ADC_SetConfigChannel(&hADC1, HAL_ADC_CHANNEL_4, &adc_channel_config);

HAL_FMAC_Init(&hfmac, (hal_fmac_t)FMAC);

sFmacConfig.x1_buf.base_half_word = INPUT_BUFFER_BASE;
sFmacConfig.x1_buf.size_half_word = INPUT_BUFFER_SIZE;
sFmacConfig.x2_buf.base_half_word = COEFFICIENT_BUFFER_BASE;
sFmacConfig.x2_buf.size_half_word = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.base_half_word  = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.size_half_word  = OUTPUT_BUFFER_BASE;

HAL_FMAC_SetConfig(&hfmac, &sFmacConfig);

data.coeff_a_size_half_word = COEFF_VECTOR_A_SIZE;
data.p_coeff_a              = aFilterCoeffA;
data.coeff_b_size_half_word = COEFF_VECTOR_B_SIZE;
data.p_coeff_b              = aFilterCoeffB;

HAL_FMAC_PreloadX2(&hfmac, &data);

/* Obtain the FMAC Write Data register (@WDATA) for ADC */
Fmac_Wdata = HAL_FMAC_GetX1Address(&hfmac);
HAL_FMAC_LockX1WriteAccessForExternPeriph(&hfmac);

HAL_FMAC_ReadY_DMA(&hfmac, Output, OutputDataSize);
HAL_FMAC_StartFilterIIR(&hfmac, COEFF_VECTOR_B_SIZE, COEFF_VECTOR_A_SIZE, GAIN, HAL_FMAC_OPT_IT_FILTER_SATURATION_ERROR);

/* Start ADC and DMA toward FMAC X1 */
HAL_ADC_Start(hadc1);
HAL_ADC_REG_StartConv_DMA(hadc1, (uint8_t const *)Fmac_Wdata, OUTPUT_BUFFER_SIZE*4);

HAL_FMAC_StopFilter(&hfmac);
HAL_FMAC_UnlockX1WriteAccessForExternPeriph(&hfmac);
HAL_FMAC_DeInit(&gh_fmac);

Key changes

  1. HAL2 supports external-peripheral feeding of X1 by providing helpers such as HAL_FMAC_GetX1Address() and explicit lock/unlock of X1 write access for external peripherals.

  2. HAL2 keeps the Y DMA read flow explicit through HAL_FMAC_ReadY_DMA() and completion callbacks.